﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Configuration.Install;
using System.Configuration;
using System.ServiceModel;
using System.IO;

namespace DotNETX.CommonWcfHost.WindowsService
{
    public partial class CommonHostService : ServiceBase
    {
        #region 常量

        public const int MAX_CONTINUAL_ERR_COUNT = 3;
        public const string CONFIG_KEY_ASSEMBLYFOLDERS = "ServiceAssemblyFolders";
        public const string CONFIG_KEY_CONTROLLER_PORT = "ControllerPort";

        #endregion

        #region 私有成员

        CommonHost commonSvcHost = null;
        Dictionary<string, int> errCounts = new Dictionary<string, int>();
        ServiceHost controllerHost = null;

        void WriteLogEntry(string message, bool error)
        {
            EventLog.WriteEntry(MyInstaller.CUSTOMSRC_NAME, message, error ? EventLogEntryType.Error : EventLogEntryType.Information);
        }

        private void OpenControllerHost()
        {
            CommonHostController.Host2Control = commonSvcHost;
            string port = ConfigurationManager.AppSettings[CONFIG_KEY_CONTROLLER_PORT];

            Uri baseAddress = new Uri(CommonHostController.CreateControllerAddress("127.0.0.1", int.Parse(port)));
            controllerHost = new ServiceHost(typeof(CommonHostController), new Uri[] { baseAddress });
            {
                System.ServiceModel.Channels.Binding binding = CommonHostController.ControllerBinding;
                controllerHost.AddServiceEndpoint(typeof(ICommonHostController), binding, baseAddress);
                controllerHost.Open();
            }
        }

        private static string[] ReadAssemblyFolders()
        {
            string strFolders = ConfigurationManager.AppSettings[CONFIG_KEY_ASSEMBLYFOLDERS];
            string[] folders = null == strFolders ? null : strFolders.Split(',');
            //if (null != folders)
            //{
            //    for (int i = 0; i < folders.Length; i++)
            //    {
            //        if (!string.IsNullOrEmpty(folders[i]) && !Path.IsPathRooted(folders[i]))
            //        {
            //            folders[i] = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory + folders[i]);
            //        }
            //    }
            //}
            return folders;
        }
        #endregion

        public CommonHostService()
        {
            base.CanStop = true;
            base.CanPauseAndContinue = false;
            base.AutoLog = false;
        }

        protected override void OnStart(string[] args)
        {
            base.OnStart(args);
            try
            {
                WriteLogEntry("开始", false);
                commonSvcHost = new CommonHost();

                commonSvcHost.Opened += (s, e1) => { WriteLogEntry(string.Format("服务[{0}]开启", e1.Name), false); };
                commonSvcHost.Closed += (s, e1) => { WriteLogEntry(string.Format("服务[{0}]开启", e1.Name), false); };
                commonSvcHost.Faulted += (s, e1) =>
                {
                    if (!errCounts.ContainsKey(e1.Name)) errCounts.Add(e1.Name, 0);
                    errCounts[e1.Name]++;
                    if (errCounts[e1.Name] < MAX_CONTINUAL_ERR_COUNT)
                    {
                        WriteLogEntry(string.Format("服务[{0}]连续{1}次开启未成功！", e1.Name, errCounts[e1.Name]), true);
                        new Action(() =>
                        {
                            System.Threading.Thread.Sleep(1000);
                            commonSvcHost.Close(e1.Name);
                            System.Threading.Thread.Sleep(2000);
                            commonSvcHost.Open(e1.Name);
                        }).BeginInvoke(null, null);
                    }
                    else
                    {
                        errCounts[e1.Name] = 0;
                        WriteLogEntry(string.Format("服务[{0}]连续{1}次开启未成功, 停止尝试开启！", e1.Name, MAX_CONTINUAL_ERR_COUNT), true);
                        commonSvcHost.Close(e1.Name);
                    }
                };
                string[] folders = ReadAssemblyFolders();
                commonSvcHost.SetServiceAssemblyFolders(folders);
                commonSvcHost.Initial();
                commonSvcHost.OpenAll();

                OpenControllerHost();
            }
            catch (Exception ex)
            {
                WriteLogEntry("错误: " + ex.Message, true);
                Stop();
            }
        }

        protected override void OnStop()
        {
            base.OnStop();
            try
            {
                if (null != commonSvcHost) commonSvcHost.CloseAll();
                if (null != controllerHost)
                {
                    controllerHost.Abort();
                    controllerHost.Close();
                }
            }
            catch (Exception ex)
            {
                WriteLogEntry("停止服务错误：" + ex.Message, true);
            }
            finally
            {
                WriteLogEntry("停止", false);
            }
        }

        protected override void OnContinue()
        {
            base.OnContinue();
            WriteLogEntry("继续", false);
        }

        protected override void OnPause()
        {
            base.OnPause();
            WriteLogEntry("暂停", false);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }


    [RunInstaller(true)]
    public class MyInstaller : Installer
    {
        #region 常量
        public const string CONFIG_KEY_SERVICENAME = "HostName";
        public const string CONFIG_KEY_SERVICEDESCRIPTION = "Description";
        #endregion

        private static string _SERVICE_NAME = null;
        private static string _DESCRIPTION = null;

        public static string SERVICE_NAME
        {
            get
            {
                if (_SERVICE_NAME == null)
                {
                    ReadConfig();
                }
                return _SERVICE_NAME;
            }
            set
            {
                _SERVICE_NAME = value;
            }
        }

        public static string DESCRIPTION
        {
            get
            {
                if (_DESCRIPTION == null)
                {
                    ReadConfig();
                }
                return _DESCRIPTION;
            }
            set
            {
                _DESCRIPTION = value;
            }
        }

        public static string CUSTOMSRC_NAME
        {
            get
            {
                return "src_" + SERVICE_NAME.ToLower().Replace(" ", "_");
            }
        }

        public static string LOG_NAME
        {
            get
            {
                return "log_" + SERVICE_NAME.Replace(" ", "");
            }
        }

        private static void ReadConfig()
        {
            string assemblyFile = (typeof(MyInstaller)).Assembly.Location;
            Configuration config = ConfigurationManager.OpenExeConfiguration(assemblyFile);
            if (config.AppSettings.Settings.AllKeys.Contains(CONFIG_KEY_SERVICENAME))
            {
                string name = config.AppSettings.Settings[CONFIG_KEY_SERVICENAME].Value;
                _SERVICE_NAME = name;
            }
            else
            {
                _SERVICE_NAME = "Common WCF Host Service";
            }
            if (config.AppSettings.Settings.AllKeys.Contains(CONFIG_KEY_SERVICEDESCRIPTION))
            {
                string des = config.AppSettings.Settings[CONFIG_KEY_SERVICEDESCRIPTION].Value;
                _DESCRIPTION = des;
            }
            else
            {
                _DESCRIPTION = "通用WCF宿主服务。";
            }
        }

        public MyInstaller()
        {
            ServiceProcessInstaller spi = new ServiceProcessInstaller();
            ServiceInstaller si = new ServiceInstaller();
            EventLogInstaller ei = new EventLogInstaller();


            spi.Account = ServiceAccount.LocalSystem;
            si.ServiceName = SERVICE_NAME;
            si.StartType = ServiceStartMode.Automatic;
            si.Description = DESCRIPTION;
            si.DisplayName = SERVICE_NAME;

            ei.Source = CUSTOMSRC_NAME;
            ei.Log = LOG_NAME;

            Installers.Add(spi);
            Installers.Add(si);
            Installers.Add(ei);

            if (EventLog.SourceExists(CUSTOMSRC_NAME))
                EventLog.DeleteEventSource(CUSTOMSRC_NAME);
        }
    }
}
